package cool.taomu.toolkit.antlr4

import java.lang.reflect.InvocationHandler
import java.lang.reflect.Proxy
import org.antlr.v4.runtime.CharStream
import org.antlr.v4.runtime.CharStreams
import org.antlr.v4.runtime.CommonTokenStream
import org.antlr.v4.runtime.Lexer
import org.antlr.v4.runtime.Parser
import org.antlr.v4.runtime.TokenStream
import org.antlr.v4.runtime.tree.ParseTree
import org.antlr.v4.runtime.tree.ParseTreeListener
import org.antlr.v4.runtime.tree.ParseTreeWalker
import org.springframework.cglib.core.ReflectUtils
import java.lang.reflect.Constructor
import java.lang.reflect.Method

class Antlr4Entry {

    Class<? extends Lexer> lexerClass
    Class<? extends Parser> parserClass
    Class<? extends ParseTreeListener> listenerInterface

    def analyzer(
        Class<? extends Lexer> lexerClass,
        Class<? extends Parser> parserClass,
        Class<? extends ParseTreeListener> listenerInterface
    ) {
        this.lexerClass = lexerClass
        this.parserClass = parserClass
        this.listenerInterface = listenerInterface
        if (!this.listenerInterface.interface) {
            var is = this.listenerInterface.interfaces
            if (is.size > 0) {
                var tclass = is.toList.findFirst [
                    it.interfaces.findFirst[it.equals(ParseTreeListener)] !== null
                ]
                this.listenerInterface = tclass as Class<? extends ParseTreeListener>
            }
        }
        return this
    }

    def entry(
        Class<?> aspectClass,
        String entryMethodName,
        String input,
        InvocationHandler handler
    ) throws Exception {

        val charStream = CharStreams.fromString(input.trim)
        val lexer = newInstance(lexerClass, #[CharStream], #[charStream]) as Lexer
        val tokens = new CommonTokenStream(lexer)
        val parser = parserClass.cast(newInstance(parserClass, #[TokenStream], #[tokens]))
        val method = parserClass.getMethod(entryMethodName)
        val tree = method.invoke(parser) as ParseTree

        val walker = new ParseTreeWalker()
        val proxyListener = Proxy.newProxyInstance(
            getClass().classLoader,
            #[listenerInterface],
            handler
        ) as ParseTreeListener

        walker.walk(proxyListener, tree)
    }

    def Object newInstance(Class<?> clazz, Class<?>[] paramTypes, Object[] params) throws Exception {
        var Constructor<?> constructor = clazz.getConstructor(paramTypes);
        return constructor.newInstance(params);
    }

    def Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws Exception {
        return clazz.getMethod(name, parameterTypes);
    }
}
